/*
 * Login
 * 
 * Version information
 *
 * Feb 17, 2006
 *
 * Copyright (c) AmSoft Systems, 2006
 */

package net.amsoft.iservice.isso.webapp.struts.action;

import java.net.URLEncoder;
import java.util.HashMap;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.amsoft.iservice.isso.client.ISSOClient;
import net.amsoft.iservice.isso.client.SAMLDataObject;
import net.amsoft.iservice.isso.hibernate.Principal;
import net.amsoft.iservice.isso.service.ISSOService;
import net.amsoft.iservice.isso.util.ISSOConstants;
import net.amsoft.iservice.isso.util.SAMLProcessor;
import net.amsoft.iservice.isso.webapp.dataObject.LoginData;
import net.amsoft.iservice.isso.webapp.dataObject.SPData;
import net.amsoft.iservice.util.IServiceUtil;
import net.amsoft.iservice.util.MetaData;
import net.amsoft.iservice.util.ResolverClient;
import net.amsoft.iservice.util.exception.IServiceException;

import org.apache.log4j.Logger;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

/**
 * Interface to receive Auth. Request from SPs (including ISSO), on receiving
 * the Auth Request this action will go with any one of four possibilities <br>
 * 
 * a) User is logged in so return artifact to SP to get assertion <br>
 * 
 * b) Else (user is not logged in and machine cookie is present) so move on to
 * password screen <br>
 * 
 * c) Else (user is not logged in, machine cookie is not present but SP is ISSO
 * itself) so move on to password screen <br>
 * 
 * d) Else (user is not logged in, machine cookie is not present and SP is not
 * ISSO), so move on to retry screen and ask him to do homepage login on ISSO
 * service <br>
 * 
 * @author rahul
 * 
 */
public class Login extends BaseAction {
	private static Logger oLogger = Logger.getLogger(Login.class);

	public ActionForward process(ActionMapping oActionMapping,
			ActionForm oActionForm, HttpServletRequest oRequest,
			HttpServletResponse oResponse) throws Exception {
		ActionForward oActionForward = null;
		ActionMessages oMessages = new ActionErrors();
		oLogger.info("process() : entry");
		try {
			// get data from authn. request
			SAMLDataObject oAuthnRequestSAMLData = SAMLProcessor
					.deserializeAuthnRequest(oRequest
							.getParameter(ISSOClient.PARAM_SAML_REQUEST));

			String sRelayState = oRequest
					.getParameter(ISSOClient.PARAM_RELAY_STATE);
			oLogger.debug("process() : Request="
					+ oRequest.getParameter(ISSOClient.PARAM_SAML_REQUEST)
					+ ", RelayState=" + sRelayState);

			LoginData oLoginData = (LoginData) oRequest.getSession(false)
					.getAttribute(ISSOConstants.SESSION_LOGIN_OBJ);
			String sIBrokerName = oRequest.getServerName();
			String sSPXri = oAuthnRequestSAMLData.getSPXri();

			// Get the Login Info of the specific relying party
			HashMap ohSPData = (HashMap) oRequest.getSession(false)
					.getAttribute(ISSOConstants.SESSION_LOGIN_HASH_OBJ);

			SPData oSPData = (SPData) ohSPData.get(sSPXri);
			if (oSPData == null) {
				oSPData = new SPData();
			}

			ohSPData.put(sSPXri, oSPData);
			oLogger.debug("process() : BrokerName=" + sIBrokerName + ", SPXRI="
					+ sSPXri);

			MetaData oMetaData = ResolverClient.getSPMetaData(sSPXri);
			String sAssertionConsumerService = oMetaData
					.getAssertionConsumerServiceURL();

			String[] arRACs = oAuthnRequestSAMLData.getArAuthnContext();
			String sComparison = oAuthnRequestSAMLData.getComparisonType();
			oLogger.debug("process() : AuthnContext=" + arRACs[0]
					+ " Comparison=" + sComparison);

            //Extract, verify i-name from HXRI, XRI
            String sIname = oAuthnRequestSAMLData.getSubjectNameIDNameQualifier();
            sIname = IServiceUtil.getIName(sIname);
            if(sIname != null){
                sIname = sIname.toLowerCase();
                oAuthnRequestSAMLData.setSubjectNameIDNameQualifier(sIname);
            }else{
                oLogger.error("process() :Invalid i-name recvd in SAML request");
                oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
                        ISSOConstants.ERR_INVALID, "i-name"));
                oActionForward = oActionMapping
                        .findForward(ISSOConstants.MESSAGE_FWD);
                saveErrors(oRequest, oMessages);
                return oActionForward;
            }
			String sAuthnContextToUse = getAuthnContextToUse(arRACs,
					sComparison);
            String sGlobalInumber = ResolverClient
                    .getGlobalINumber(sIname);
			String sArtifactToken = createArtifact(ResolverClient
					.getFinalMetaData(sGlobalInumber).getProviderID());

			// Check if "No Authentication Context"
			if (SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS
					.equalsIgnoreCase(sAuthnContextToUse)) {
				oActionForward = getForwardForNegativeResponse(oRequest,
						oAuthnRequestSAMLData, oActionMapping, oMetaData,
						sArtifactToken, sRelayState,
						SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS);
			}

			oLogger.debug("process() : GlobalInumber=" + sGlobalInumber);

			if (sGlobalInumber == null || sAssertionConsumerService == null) {
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						ISSOConstants.ERR_INAME_NOT_FOUND, true));
				saveMessages(oRequest, oMessages);
				oLogger.error("process() : GIN=" + sGlobalInumber
						+ ", Message : Returning to iname page");
				return new ActionForward(ISSOConstants.INAME_FWD);
			}
			oAuthnRequestSAMLData
					.setAssertionConsumerServiceURL(sAssertionConsumerService);

			oActionForward = getActionForward(oRequest, oResponse, oLoginData,
					oAuthnRequestSAMLData, oActionMapping, oMetaData,
					sAssertionConsumerService, sArtifactToken, sGlobalInumber,
					sAuthnContextToUse, sComparison);

		} catch (Exception e) {
			oLogger.warn("process() : in Exception=true");
			if (e instanceof IServiceException) {
				oLogger.error("process() : IServiceException=true, Message :"+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						((IServiceException) e).getMessage(), false));
				oActionForward = oActionMapping.getInputForward();
			} else {
				oLogger.debug("process() : IServiceException=false, Message :"+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						ISSOConstants.ERR_INVALID_REQUEST));
				oActionForward = oActionMapping
						.findForward(ISSOConstants.MESSAGE_FWD);
			}
			saveMessages(oRequest, oMessages);
		}
		oLogger.info("process() : exit");
		return oActionForward;
	} // process() ends

	/**
	 * Returns Hash-separated List of Authentication Contexts that can be used
	 * according to given input parameters
	 * 
	 * @param arRACs
	 *            Array of Requested Authentication Contexts by relying party
	 * @param sComparison
	 *            Comparison type - exact or minimum or maximum or better
	 * @return String
	 */
	private String getAuthnContextToUse(String[] arRACs, String sComparison) {
		oLogger.info("getAuthnContextToUse() : entry");
		String sAuthnContextToUse = SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS;
		int[] arRACsPriority = getRACsPriorityArray(arRACs);

		// Check if there is no valid requested authentication context
		if (noValidRAC(arRACsPriority)) {
			oLogger
					.debug("getAuthnContextToUse() : None of the Requested Authentication Contexts exist");
			sAuthnContextToUse = SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS;
		} else {
			if (ISSOConstants.RAC_EXACT.equalsIgnoreCase(sComparison))
				sAuthnContextToUse = getExactAuthnContext(arRACsPriority);
			else if (ISSOConstants.RAC_MINIMUM.equalsIgnoreCase(sComparison))
				sAuthnContextToUse = getMinimumAuthnContext(arRACsPriority);
			else if (ISSOConstants.RAC_MAXIMUM.equalsIgnoreCase(sComparison))
				sAuthnContextToUse = getMaximumAuthnContext(arRACsPriority);
			else if (ISSOConstants.RAC_BETTER.equalsIgnoreCase(sComparison))
				sAuthnContextToUse = getBetterAuthnContext(arRACsPriority);
		}

		oLogger.debug("getAuthnContextToUse(): Returning AuthnContext="
				+ sAuthnContextToUse);
		return sAuthnContextToUse;
	}

	/**
	 * Returns true if none of the Requested Authentication Contexts is valid
	 * otherwise returns false
	 * 
	 * @param arRACsPriority
	 *            Array of priorities of the requested authentication contexts
	 * @return boolean
	 */
	private boolean noValidRAC(int[] arRACsPriority) {
		for (int i = 0; i < arRACsPriority.length; i++)
			if (arRACsPriority[i] != -1)
				return false;
		return true;
	}

	/**
	 * Returns Authentication Context to be used when comparison type is "exact"
	 * 
	 * @param arRACsPriority
	 *            Array of priorities of the requested authentication contexts
	 * @return String
	 */
	private String getExactAuthnContext(int[] arRACsPriority) {
		oLogger.info("getExactAuthnContext() : entry");
		int priorityPrevSession = ISSOConstants
				.getPriority(ISSOConstants.RAC_PREVIOUS_SESSION);
		int priorityPasswordProtected = ISSOConstants
				.getPriority(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
		String sReturnAuthnContext = "";

		for (int i = 0; i < arRACsPriority.length; i++) {
			int priorityRequested = arRACsPriority[i];
			if (priorityRequested != -1) {
				if (priorityRequested == priorityPrevSession)
					sReturnAuthnContext = ISSOConstants.RAC_PREVIOUS_SESSION;
				else if (priorityRequested == priorityPasswordProtected)
					sReturnAuthnContext = ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT;
			}
		}

		if (sReturnAuthnContext.length() == 0)
			sReturnAuthnContext = SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS;

		return sReturnAuthnContext;
	}

	/**
	 * Returns Hash-separated List of Authentication Contexts that can be used
	 * when comparison type is "minimum"
	 * 
	 * @param arRACsPriority
	 *            Array of priorities of the requested authentication contexts
	 * @return String
	 */
	private String getMinimumAuthnContext(int[] arRACsPriority) {
		oLogger.info("getMinimumAuthnContext() : entry");
		int priorityPrevSession = ISSOConstants
				.getPriority(ISSOConstants.RAC_PREVIOUS_SESSION);
		int priorityPasswordProtected = ISSOConstants
				.getPriority(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
		String sReturnAuthnContext = "";

		for (int i = 0; i < arRACsPriority.length; i++) {
			int priorityRequested = arRACsPriority[i];
			if (priorityRequested != -1) {
				if (priorityRequested >= priorityPrevSession) {
					if (sReturnAuthnContext
							.indexOf(ISSOConstants.RAC_PREVIOUS_SESSION) < 0)
						sReturnAuthnContext = (sReturnAuthnContext.equals("")) ? ISSOConstants.RAC_PREVIOUS_SESSION
								: (sReturnAuthnContext + "#" + ISSOConstants.RAC_PREVIOUS_SESSION);
				}
				if (priorityRequested >= priorityPasswordProtected) {
					if (sReturnAuthnContext
							.indexOf(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT) < 0)
						sReturnAuthnContext = (sReturnAuthnContext.equals("")) ? ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT
								: (sReturnAuthnContext + "#" + ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
				}
			}
		}

		if (sReturnAuthnContext.length() == 0)
			sReturnAuthnContext = SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS;

		return sReturnAuthnContext;
	}

	/**
	 * Returns Hash-separated List of Authentication Contexts that can be used
	 * when comparison type is "maximum"
	 * 
	 * @param arRACsPriority
	 *            Array of priorities of the requested authentication contexts
	 * @return String
	 */
	private String getMaximumAuthnContext(int[] arRACsPriority) {
		oLogger.info("getMaximumAuthnContext() : entry");
		int priorityPrevSession = ISSOConstants
				.getPriority(ISSOConstants.RAC_PREVIOUS_SESSION);
		int priorityPasswordProtected = ISSOConstants
				.getPriority(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
		String sReturnAuthnContext = "";

		for (int i = 0; i < arRACsPriority.length; i++) {
			int priorityRequested = arRACsPriority[i];
			if (priorityRequested != -1) {
				if (priorityRequested <= priorityPrevSession) {
					if (sReturnAuthnContext
							.indexOf(ISSOConstants.RAC_PREVIOUS_SESSION) < 0)
						sReturnAuthnContext = (sReturnAuthnContext.equals("")) ? ISSOConstants.RAC_PREVIOUS_SESSION
								: (sReturnAuthnContext + "#" + ISSOConstants.RAC_PREVIOUS_SESSION);
				}
				if (priorityRequested <= priorityPasswordProtected) {
					if (sReturnAuthnContext
							.indexOf(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT) < 0)
						sReturnAuthnContext = (sReturnAuthnContext.equals("")) ? ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT
								: (sReturnAuthnContext + "#" + ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
				}
			}
		}

		if (sReturnAuthnContext.length() == 0)
			sReturnAuthnContext = SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS;

		return sReturnAuthnContext;
	}

	/**
	 * Returns Hash-separated List of Authentication Contexts that can be used
	 * when comparison type is "better"
	 * 
	 * @param arRACsPriority
	 *            Array of priorities of the requested authentication contexts
	 * @return String
	 */
	private String getBetterAuthnContext(int[] arRACsPriority) {
		oLogger.info("getBetterAuthnContext() : entry");
		int priorityPrevSession = ISSOConstants
				.getPriority(ISSOConstants.RAC_PREVIOUS_SESSION);
		int priorityPasswordProtected = ISSOConstants
				.getPriority(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
		String sReturnAuthnContext = "";

		for (int i = 0; i < arRACsPriority.length; i++) {
			int priorityRequested = arRACsPriority[i];
			if (priorityRequested != -1) {
				if (priorityRequested > priorityPrevSession) {
					if (sReturnAuthnContext
							.indexOf(ISSOConstants.RAC_PREVIOUS_SESSION) < 0)
						sReturnAuthnContext = (sReturnAuthnContext.equals("")) ? ISSOConstants.RAC_PREVIOUS_SESSION
								: (sReturnAuthnContext + "#" + ISSOConstants.RAC_PREVIOUS_SESSION);
				}
				if (priorityRequested > priorityPasswordProtected) {
					if (sReturnAuthnContext
							.indexOf(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT) < 0)
						sReturnAuthnContext = (sReturnAuthnContext.equals("")) ? ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT
								: (sReturnAuthnContext + "#" + ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT);
				}
			}
		}

		if (sReturnAuthnContext.length() == 0)
			sReturnAuthnContext = SAMLProcessor.SAML_NO_AUTHN_CONTEXT_STATUS;

		return sReturnAuthnContext;
	}

	/**
	 * Returns Array of priorities of the requested authentication contexts
	 * 
	 * @param arRACs
	 *            Array of all the requested authentication contexts
	 * @return int[]
	 */
	private int[] getRACsPriorityArray(String[] arRACs) {
		int[] arPriorities = new int[arRACs.length];
		for (int i = 0; i < arRACs.length; i++)
			arPriorities[i] = ISSOConstants.getPriority(arRACs[i]);
		return arPriorities;
	}

	/**
	 * Checks various possible conditions according to the requested
	 * authentication contexts and then returns the correct ActionForward object
	 * to be followed
	 * 
	 * @param oRequest
	 *            HTTPServletRequest Object
	 * @param oResponse
	 *            HTTPServletResponse Object
	 * @param oLoginData
	 *            LoginData Object holding Login information
	 * @param oAuthnRequestSAMLData
	 *            SAMLData Object holding SAML element
	 * @param oActionMapping
	 *            ActionMapping Object
	 * @param sAssertionConsumerService
	 *            AssertionConsumerService
	 * @param sArtifactToken
	 *            Artifact Token String
	 * @param sGlobalInumber
	 *            Global Inumber
	 * @return ActionForward
	 */
	private ActionForward getActionForward(HttpServletRequest oRequest,
			HttpServletResponse oResponse, LoginData oLoginData,
			SAMLDataObject oAuthnRequestSAMLData, ActionMapping oActionMapping,
			MetaData oMetaData, String sAssertionConsumerService,
			String sArtifactToken, String sGlobalInumber,
			String sAuthnContextToUse, String sComparison) {
		oLogger.info("getActionForward() : entry");
		ActionForward oActionForward = null;
		String sSPXri = oAuthnRequestSAMLData.getSPXri();
		String sIBrokerName = oRequest.getServerName();
		String sRelayState = oRequest
				.getParameter(ISSOClient.PARAM_RELAY_STATE);
		String sMachineCookie = oLoginData.getMachineCookie();
		String sSessionCookie = oLoginData.getSessionCookie();
		boolean bRememberMe = true;
		if (sMachineCookie == null) {
			sMachineCookie = IServiceUtil.generateUniqueID();
			bRememberMe = false;
		}
		oLogger.debug("getActionForward() : SessionCokie=" + sSessionCookie
				+ ", MachineCookie=" + sMachineCookie);

		// Check if PreviousSession or PasswordProtected
		oLogger.info("getActionForward() : AuthnContext=" + sAuthnContextToUse);
		if (sAuthnContextToUse.indexOf(ISSOConstants.RAC_PREVIOUS_SESSION) > -1) {
			oLogger
					.info("getActionForward() : Processing for Previous Session");
			if (verifyCookieState(sGlobalInumber, sSessionCookie, true)) {
				oLogger.info("getActionForward() : Valid PreviousSession=true");
				oActionForward = getForwardForValidSession(oRequest, oResponse,
						oLoginData, oAuthnRequestSAMLData, oActionMapping,
						sAssertionConsumerService, sArtifactToken,
						sGlobalInumber, sRelayState, bRememberMe);
			} else {
				// no session exist,
				if (sAuthnContextToUse
						.indexOf(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT) > -1) {
					// if its Special SP then move to password page,
					// else check machine
					if (checkSpecialSPCase(oRequest, sIBrokerName, sSPXri)) {
						oLogger.info("getActionForward() : SpecialSp = true");
						oActionForward = getForwardForSpecialSP(oRequest,
								oAuthnRequestSAMLData, oActionMapping,
								sGlobalInumber);
					} else {
						oLogger.info("getActionForward() : SpecialSp = false");
						oActionForward = getForwardForOrdinarySP(oRequest,
								oResponse, oAuthnRequestSAMLData,
								oActionMapping, sGlobalInumber, sRelayState,
								sMachineCookie);
					}
				} else {
					// No Previous Session and Cannot use PasswordProtected
					oLogger.info("getActionForward() : PreviousSession=false");
					oActionForward = getForwardForNegativeResponse(oRequest,
							oAuthnRequestSAMLData, oActionMapping, oMetaData,
							sArtifactToken, sRelayState,
							SAMLProcessor.SAML_AUTH_FAIL_STATUS);
				}
			}
		} else if (sAuthnContextToUse
				.indexOf(ISSOConstants.RAC_PASSWORD_PROTECTED_TRANSPORT) > -1) {
			oLogger
					.info("getActionForward() : Processing for PasswordProtectedTransport");
			if (checkSpecialSPCase(oRequest, sIBrokerName, sSPXri)) {
				oLogger.info("getActionForward() : SpecialSp=true");
				oActionForward = getForwardForSpecialSP(oRequest,
						oAuthnRequestSAMLData, oActionMapping, sGlobalInumber);
			} else {
				oLogger.info("getActionForward() : SpecialSp=false");
				oActionForward = getForwardForOrdinarySP(oRequest, oResponse,
						oAuthnRequestSAMLData, oActionMapping, sGlobalInumber,
						sRelayState, sMachineCookie);
			}
		} else {
			// No Previous Session and No PasswordProtected
			oLogger
					.info("getActionForward() : PreviousSession=false & PasswordProtected=false");
			oActionForward = getForwardForNegativeResponse(oRequest,
					oAuthnRequestSAMLData, oActionMapping, oMetaData,
					sArtifactToken, sRelayState,
					SAMLProcessor.SAML_AUTH_FAIL_STATUS);
		}
		return oActionForward;
	}

	/**
	 * Gets called when a valid previous session exists for the user and returns
	 * forward mapping to the homepage of the service
	 * 
	 * @param oRequest
	 *            HTTPServletRequest Object
	 * @param oResponse
	 *            HTTPServletResponse Object
	 * @param oLoginData
	 *            LoginData Object holding Login information
	 * @param oAuthnRequestSAMLData
	 *            SAMLData Object holding SAML element
	 * @param oActionMapping
	 *            ActionMapping Object
	 * @param sAssertionConsumerService
	 *            AssertionConsumerService
	 * @param sArtifactToken
	 *            Artifact Token String
	 * @param sGlobalInumber
	 *            Global Inumber
	 * @param sRelayState
	 *            Relay State String
	 * @param bRememberMe
	 *            True if user had checked the checkbox to remember machine
	 * @return ActionForward
	 */
	private ActionForward getForwardForValidSession(
			HttpServletRequest oRequest, HttpServletResponse oResponse,
			LoginData oLoginData, SAMLDataObject oAuthnRequestSAMLData,
			ActionMapping oActionMapping, String sAssertionConsumerService,
			String sArtifactToken, String sGlobalInumber, String sRelayState,
			boolean bRememberMe) {
		oLogger.info("getForwardForValidSession() : entry");
		ActionForward oActionForward = null;
		ActionMessages oMessages = new ActionErrors();
		try {
			String sIBrokerName = oRequest.getServerName();
			String sMachineCookie = oLoginData.getMachineCookie();
			String sSessionCookie = oLoginData.getSessionCookie();
			HashMap ohSPData = (HashMap) oRequest.getSession(false)
					.getAttribute(ISSOConstants.SESSION_LOGIN_HASH_OBJ);
			String sSPXri = oAuthnRequestSAMLData.getSPXri();
			SPData oSPData = (SPData) ohSPData.get(sSPXri);

			if (sMachineCookie == null) {
				sMachineCookie = IServiceUtil.generateUniqueID();
			}

			// session is verified, so make a login
			ISSOService.login(sGlobalInumber, null, sIBrokerName,
					sSessionCookie, sMachineCookie, bRememberMe);
			oLogger.info("getForwardForValidSession() : Login Successful");

			// login successfully done
			int iReputationScore = -1;
			if (oAuthnRequestSAMLData.isReputationScoreRequired()) {
				iReputationScore = ISSOService.getReputationScore(
						sGlobalInumber, sSessionCookie);
			}
			String sResponseID = IServiceUtil.generateUniqueID();
			String sAssertionID = IServiceUtil.generateUniqueID();
			oLogger.info("getForwardForValidSession() : ResponseID="
					+ sResponseID + ", ArtifactToken=" + sArtifactToken);
			// create saml response
			String sResponse = SAMLProcessor.serializePositiveAuthnLResponse(
					oAuthnRequestSAMLData.getAssertionConsumerServiceURL(),
					oAuthnRequestSAMLData.getID(), ISSOConstants
							.getProviderID(sIBrokerName), sGlobalInumber,
					sSessionCookie, oAuthnRequestSAMLData
							.isReputationScoreRequired(), iReputationScore,
					sResponseID, sAssertionID);

			// save response associated with a artifcat
			ISSOService.saveSAMLResponse(sArtifactToken, sSessionCookie,
					sResponse);

			// set the cookies
			setCookies(oResponse, sSessionCookie, sMachineCookie, true);

			// create artifact document to be sent over redirect
			String sArtifactDocXML = SAMLProcessor
					.serializeArtifactDocument(sArtifactToken);
			ohSPData.put(sSPXri, oSPData);

			// make a redirect to SP's assertion consumer sservice
			oActionForward = new ActionForward(sAssertionConsumerService + "?"
					+ ISSOClient.PARAM_SAML_ART + "="
					+ URLEncoder.encode(sArtifactDocXML, "UTF-8") + "&"
					+ ISSOClient.PARAM_RELAY_STATE + "="
					+ URLEncoder.encode(sRelayState, "UTF-8"), true);
			oLogger.info("getForwardForValidSession() : Exit=Redirect to SSO");
		} catch (Exception e) {
			if (e instanceof IServiceException) {
				oLogger
						.error("getForwardForValidSession() : IServiceException=true, Message : "+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						((IServiceException) e).getMessage(), false));
				oActionForward = oActionMapping.getInputForward();
			} else {
				oLogger
						.debug("getForwardForValidSession() : IServiceException=false, Message : "+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						ISSOConstants.ERR_INVALID_REQUEST));
				oActionForward = oActionMapping
						.findForward(ISSOConstants.MESSAGE_FWD);
			}
			saveMessages(oRequest, oMessages);
		}
		return oActionForward;
	}

	/**
	 * Gets called when SP is ISSO itself and returns forward mapping to ISSO
	 * password screen
	 * 
	 * @param oRequest
	 *            HTTPServletRequest Object
	 * @param oAuthnRequestSAMLData
	 *            SAMLData Object holding SAML element
	 * @param oActionMapping
	 *            ActionMapping Object
	 * @param sGlobalInumber
	 *            Global Inumber
	 * @return ActionForward
	 */
	private ActionForward getForwardForSpecialSP(HttpServletRequest oRequest,
			SAMLDataObject oAuthnRequestSAMLData, ActionMapping oActionMapping,
			String sGlobalInumber) {
		oLogger.info("getForwardForSpecialSP() : entry");
		ActionForward oActionForward = null;
		ActionMessages oMessages = new ActionErrors();
		try {
			HashMap ohSPData = (HashMap) oRequest.getSession(false)
					.getAttribute(ISSOConstants.SESSION_LOGIN_HASH_OBJ);
			String sSPXri = oAuthnRequestSAMLData.getSPXri();
			SPData oSPData = (SPData) ohSPData.get(sSPXri);
			oSPData.setGlobalInumber(sGlobalInumber);
			oSPData.setSAMLData(oAuthnRequestSAMLData);
			oRequest.setAttribute(ISSOConstants.REQ_PARAM_SPXRI, sSPXri);
			oRequest.setAttribute("SPData", oSPData);
			ohSPData.put(sSPXri, oSPData);
			oActionForward = oActionMapping
					.findForward(ISSOConstants.SUCCESS_FWD);
		} catch (Exception e) {
			if (e instanceof IServiceException) {
				oLogger
						.warn("getForwardForSpecialSP() : IServiceException=true, Message : "+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						((IServiceException) e).getMessage(), false));
				oActionForward = oActionMapping.getInputForward();
			} else {
				oLogger
						.debug("getForwardForSpecialSP() : IServiceException=false, Message : "+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						ISSOConstants.ERR_INVALID_REQUEST));
				oActionForward = oActionMapping
						.findForward(ISSOConstants.MESSAGE_FWD);
			}
			saveMessages(oRequest, oMessages);
		}
		return oActionForward;
	}

	/**
	 * Gets called when SP is other than ISSO. Further checks if there exists a
	 * valid machine cookie or not and calls other methods accordingly
	 * 
	 * @param oRequest
	 *            HTTPServletRequest Object
	 * @param response
	 * @param oAuthnRequestSAMLData
	 *            SAMLData Object holding SAML element
	 * @param oActionMapping
	 *            ActionMapping Object
	 * @param sGlobalInumber
	 *            Global Inumber
	 * @param sRelayState
	 *            Relay State String
	 * @param sMachineCookie
	 *            Machine Cookie String
	 * @return ActionForward
	 */
	private ActionForward getForwardForOrdinarySP(HttpServletRequest oRequest,
			HttpServletResponse oResponse,
			SAMLDataObject oAuthnRequestSAMLData, ActionMapping oActionMapping,
			String sGlobalInumber, String sRelayState, String sMachineCookie) {
		oLogger.info("getForwardForOrdinarySP() : entry");
		String sSPXri = oAuthnRequestSAMLData.getSPXri();
		HashMap ohSPData = (HashMap) oRequest.getSession(false).getAttribute(
				ISSOConstants.SESSION_LOGIN_HASH_OBJ);
		SPData oSPData = (SPData) ohSPData.get(sSPXri);

		oSPData.setRelayState(sRelayState);
		oSPData.setGlobalInumber(sGlobalInumber);
		oSPData.setSAMLData(oAuthnRequestSAMLData);
		oRequest.setAttribute("SPData", oSPData);
		oRequest.setAttribute(ISSOConstants.REQ_PARAM_SPXRI, sSPXri);
		ohSPData.put(sSPXri, oSPData);

		if (verifyCookieState(sGlobalInumber, sMachineCookie, false)) {
			oLogger.info("getForwardForOrdinarySP() : VerifyCookieState=true");
			return getForwardForValidMachineCookie(oRequest,
					oAuthnRequestSAMLData, oActionMapping, sGlobalInumber,
					sRelayState);
		} else {
			oLogger.info("getForwardForOrdinarySP() : VerifyCookieState=false");
			oRequest.setAttribute(ISSOConstants.REQ_ATTRIB_IS_PASSIVE,
					oAuthnRequestSAMLData.isPassive());
			oRequest.setAttribute(ISSOConstants.REQ_ATTRIB_PROVIDER_NAME,
					oAuthnRequestSAMLData.getProviderName());

			// Cookie created for same page login
			Cookie oSamePage = new Cookie(ISSOConstants.SERV_ID_COOKIE_NAME,
					sSPXri);
			oSamePage.setMaxAge(ISSOConstants.VALIDITY_SERVICE_COOKIE);
			oSamePage.setSecure(true);
			oResponse.addCookie(oSamePage);
			return oActionMapping.findForward(ISSOConstants.RETRY_FWD);
		}
	}

	/**
	 * Gets called when SP is other than ISSO and there exists a valid machine
	 * cookie
	 * 
	 * @param oRequest
	 *            HTTPServletRequest Object
	 * @param oAuthnRequestSAMLData
	 *            SAMLData Object holding SAML element
	 * @param oActionMapping
	 *            ActionMapping Object
	 * @param sGlobalInumber
	 *            Global Inumber
	 * @param sRelayState
	 *            Relay State String
	 * @return ActionForward
	 */
	private ActionForward getForwardForValidMachineCookie(
			HttpServletRequest oRequest, SAMLDataObject oAuthnRequestSAMLData,
			ActionMapping oActionMapping, String sGlobalInumber,
			String sRelayState) {
		oLogger.info("getForwardForValidMachineCookie() : entry");
		ActionForward oActionForward = null;
		ActionMessages oMessages = new ActionErrors();
		try {
			Principal oPrincipal = ISSOService.getPrincipalSecurityInfo(
					sGlobalInumber, null);
			String sGreeting = oPrincipal.getUnEncryptedGreeting();

			oRequest.getSession(false).setAttribute(
					ISSOConstants.REQ_ATTRIB_GREETING, sGreeting);
			oRequest.getSession(false).setAttribute(
					ISSOConstants.SESSION_SHOW_GREETING, "true");
			oActionForward = oActionMapping
					.findForward(ISSOConstants.SUCCESS_FWD);
		} catch (Exception e) {
			if (e instanceof IServiceException) {
				oLogger
						.warn("getForwardForValidMachineCookie() : IServiceException=true, Message : "+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						((IServiceException) e).getMessage(), false));
				oActionForward = oActionMapping.getInputForward();
			} else {
				oLogger
						.debug("getForwardForValidMachineCookie() : IServiceException=false, Message : "+e.getMessage());
				oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						ISSOConstants.ERR_INVALID_REQUEST));
				oActionForward = oActionMapping
						.findForward(ISSOConstants.MESSAGE_FWD);
			}
			saveMessages(oRequest, oMessages);
		}
		return oActionForward;
	}

	/**
	 * Gets called when a negative response has to be generated
	 * 
	 * @param oRequest
	 *            HTTPServletRequest Object
	 * @param oAuthnRequestSAMLData
	 *            SAMLData Object holding SAML element
	 * @param oActionMapping
	 *            ActionMapping Object
	 * @param oMetaData
	 *            Principal MetaData Object
	 * @param sArtifactToken
	 *            Artifact Token String
	 * @param sRelayState
	 *            Relay State String
	 * @param sStatus
	 *            SAML Status
	 * @return ActionForward
	 */
	private ActionForward getForwardForNegativeResponse(
			HttpServletRequest oRequest, SAMLDataObject oAuthnRequestSAMLData,
			ActionMapping oActionMapping, MetaData oMetaData,
			String sArtifactToken, String sRelayState, String sStatus) {
		oLogger.info("getForwardForNegativeResponse() : entry");
		ActionForward oActionForward = null;
		ActionMessages oMessages = new ActionErrors();
		try {
			// Create a negative response
			oLogger.info("getForwardForNegativeResponse() : Status" + sStatus);
			String sNegativeResponse = SAMLProcessor
					.serializeNegativeAuthnResponse(oMetaData
							.getAssertionConsumerServiceURL(),
							oAuthnRequestSAMLData.getID(), ISSOConstants
									.getProviderID(oRequest.getServerName()),
							IServiceUtil.generateUniqueID(), sStatus);

			String sArtifactDocXML = SAMLProcessor
					.serializeArtifactDocument(sArtifactToken);
			oLogger.info("getForwardForNegativeResponse() : Artifact="
					+ sArtifactToken);
			ISSOService.saveSAMLResponse(sArtifactToken, null,
					sNegativeResponse);

			oLogger
					.info("getForwardForNegativeResponse() : NegativeSAMLResponse=Saved");

			// Send back to relying party
			try {
				oActionForward = new ActionForward(oMetaData
						.getAssertionConsumerServiceURL()
						+ "?"
						+ ISSOClient.PARAM_SAML_ART
						+ "="
						+ URLEncoder.encode(sArtifactDocXML, "UTF-8")
						+ "&"
						+ ISSOClient.PARAM_RELAY_STATE
						+ "="
						+ URLEncoder.encode(sRelayState, "UTF-8"), true);
				oLogger
						.info("getForwardForNegativeResponse() : SendRedirect=true");
				return oActionForward;
			} catch (Exception e) {
				oLogger
						.warn("getForwardForNegativeResponse() : Exception=URLCreation");
			}
		} catch (IServiceException e) {
			oLogger
					.warn("getForwardForNegativeResponse() : IServiceException=true, Message : "+e.getMessage());
			oMessages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
					((IServiceException) e).getMessage(), false));
			oActionForward = oActionMapping.getInputForward();
			saveMessages(oRequest, oMessages);
		}
		return oActionForward;
	}
}